﻿/*
 *
 * DX_SourceOutliner
 * Copyright (C)2009
 * http://code.google.com/p/dxcsourceoutliner
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using DevExpress.CodeRush.Core;
using DevExpress.CodeRush.StructuralParser;

namespace DX_SourceOutliner
{
    public class DeprecatedElementTreeBuilder
    {
        private ICollection<TreeNode> _rootNodes;

        private ICollection<MemberVisibility> _visibilitiesWithCustomIcons = new List<MemberVisibility>();

        /// <summary>
        /// Initializes a new instance of the ElementTree class.
        /// </summary>
        public DeprecatedElementTreeBuilder()
        {
            IncludeParameterNamesInMethods = false;
            IncludeReturnTypesInMethods = false;

            _visibilitiesWithCustomIcons.Add(MemberVisibility.Public);
            _visibilitiesWithCustomIcons.Add(MemberVisibility.Protected);
            _visibilitiesWithCustomIcons.Add(MemberVisibility.Private);
        }

        public bool IncludeParameterNamesInMethods { get; set; }

        public bool IncludeReturnTypesInMethods { get; set; }

        public TreeNode CreateDocumentNode(TextDocument document)
        {
            TreeNode documentNode = new TreeNode { Text = document.Name, Name = document.Name };

            switch (System.IO.Path.GetExtension(document.Name).ToLower())
            {
                case ".cs":
                    documentNode.ImageKey = "Document_CS";
                    break;
                case ".vb":
                    documentNode.ImageKey = "Document_VB";
                    break;
                case ".js":
                    documentNode.ImageKey = "Document_JS";
                    break;
                default:
                    documentNode.ImageKey = "Document";
                    break;
            }

            return documentNode;
        }

        public IEnumerable<TreeNode> GenerateTreeNodes(TextDocument document)
        {
            _rootNodes = new List<TreeNode>();

            if (document != null && !CodeRush.Designer.IsActive)
            {
                foreach (var item in document.FileNode.Nodes)
                {
                    CodeElement element = item as CodeElement;
                    if (element != null && element.Parent == document.FileNode)
                    {
                        TreeNode elementNode = CreateTreeNodeForElement(element);
                        if (elementNode != null)
                            _rootNodes.Add(elementNode);
                    }
                }
            }

            return _rootNodes;
        }

        public IEnumerable<TreeNode> GenerateTreeNodes()
        {
            IEnumerable<TreeNode> nodes = new List<TreeNode>();

            foreach (TextDocument document in CodeRush.Documents.AllTextDocuments)
            {
                nodes = GenerateTreeNodes(document);
            }

            return nodes;
        }

        private TreeNode AddChildren(TreeNode parent, LanguageElement element)
        {
            foreach (LanguageElement child in element.Nodes)
            {
                TreeNode generatedNode = CreateTreeNodeForElement(child as CodeElement);
                if (generatedNode != null)
                    parent.Nodes.Add(generatedNode);
            }

            return parent;
        }

        private string AppendTypeDeclaration(LanguageElement element, string elementNodeText)
        {
            string typeDeclaration = GetTypeDeclarationText(element);

            return IncludeReturnTypesInMethods ? string.Format("{0} : {1}", elementNodeText, typeDeclaration) : elementNodeText;
        }

        private TreeNode CreateTreeNodeForElement(CodeElement element)
        {
            if (element == null)
                return null;

            TreeNode workingNode = null;

            switch (element.ElementType)
            {
                case LanguageElementType.Namespace:
                    {
                        Namespace namespaceElement = element as Namespace;

                        if (namespaceElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(namespaceElement, false);

                    }
                    break;

                case LanguageElementType.Method:
                    {
                        Method methodElement = element as Method;

                        if (methodElement != null)
                            workingNode = CreateTreeNodeWithoutChildrenButWithParameters(methodElement, true);
                    }
                    break;

                case LanguageElementType.Module:
                    {
                        Module moduleElement = element as Module;

                        if (moduleElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(moduleElement, false);
                    }
                    break;

                case LanguageElementType.Class:
                    {
                        Class classElement = element as Class;

                        if (classElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(classElement, false);
                    }
                    break;

                case LanguageElementType.Variable:
                case LanguageElementType.InitializedVariable:
                case LanguageElementType.Const:
                    {
                        Variable variableElement = element as Variable;

                        if (variableElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(variableElement, true);
                    }
                    break;

                case LanguageElementType.Struct:
                    {
                        Struct structElement = element as Struct;

                        if (structElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(structElement, false);
                    }
                    break;

                case LanguageElementType.Property:
                    {
                        Property propertyElement = element as Property;

                        if (propertyElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(propertyElement, true);
                    }
                    break;

                case LanguageElementType.Delegate:
                    {
                        DelegateDefinition delegateElement = element as DelegateDefinition;

                        if (delegateElement != null)
                            workingNode = CreateTreeNodeWithoutChildrenButWithParameters(delegateElement, true);
                    }
                    break;

                case LanguageElementType.Interface:
                    {
                        Interface interfaceElement = element as Interface;

                        if (interfaceElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(interfaceElement, false);
                    }
                    break;

                case LanguageElementType.Enum:
                    {
                        Enumeration enumerationElement = element as Enumeration;

                        if (enumerationElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(enumerationElement, false);
                    }
                    break;

                case LanguageElementType.EnumElement:
                    {
                        EnumElement enumerationElementElement = element as EnumElement;

                        if (enumerationElementElement != null)
                            workingNode = CreateTreeNodeWithChildrenButWithoutParameters(enumerationElementElement, false);
                    }
                    break;

                case LanguageElementType.Event:
                    {
                        Event eventElement = element as Event;

                        if (eventElement != null)
                            workingNode = CreateTreeNodeWithoutChildrenButWithParameters(eventElement, true);
                    }
                    break;

                default:
                    break;
            }

            if (workingNode != null)
                workingNode.Name = workingNode.Text;

            return workingNode;

        }

        private TreeNode CreateTreeNodeWithChildrenButWithoutParameters<T>(T element, bool hasReturnType) where T : LanguageElement
        {
            TreeNode node = new TreeNode() { Tag = element };
            node = SetIconsForTreeNode(node);
            node = AddChildren(node, element);

            if (hasReturnType)
            {
                node.Text = AppendTypeDeclaration(element, element.Name);
                node.ToolTipText = GetToolTipTextWithReturnType(element);
            }
            else
            {
                node.Text = element.Name;
                node.ToolTipText = GetToolTipTextWithoutReturnType(element);
            }

            return node;

        }

        private TreeNode CreateTreeNodeWithoutChildrenButWithParameters<T>(T element, bool hasReturnType) where T : MemberWithParameters
        {
            TreeNode node = null;

            if (element != null)
            {
                node = new TreeNode() { Tag = element };
                node = SetIconsForTreeNode(node);

                string paramString = GetParameterText(element);

                if (hasReturnType)
                {
                    node.Text = AppendTypeDeclaration(element, string.Format("{0} ({1})", element.Name, paramString));
                    node.ToolTipText = GetToolTipTextWithReturnType(element);
                }
                else
                {
                    node.Text = element.Name;
                    node.ToolTipText = GetToolTipTextWithoutReturnType(element);
                }
            }

            return node;
        }

        private string GetMemberVisibilityText(IMemberElement element)
        {
            return element.Visibility.ToString();
        }

        private string GetMemberVisibilityText(LanguageElement element)
        {
            var memberElement = element as IMemberElement;
            return memberElement != null ? GetMemberVisibilityText(memberElement) : string.Empty;
        }

        private string GetParameterText(MemberWithParameters member)
        {
            string paramString = string.Empty;

            foreach (LanguageElement item in member.Parameters)
            {
                string parameterType = parameterType = CodeRush.Language.GetSimpleTypeName(item.GetTypeName());

                if (parameterType == string.Empty)
                    parameterType = "object";

                if (IncludeParameterNamesInMethods)
                {
                    paramString += string.Format("{0} {1}, ", parameterType, item.Name);
                }
                else
                    paramString += string.Format("{0}, ", parameterType);
            }

            if (paramString.EndsWith(", "))
                paramString = paramString.Remove(paramString.Length - 2);
            return paramString;
        }

        private string GetToolTipTextWithoutReturnType(LanguageElement element)
        {
            return string.Format("{0} {1}", GetMemberVisibilityText(element as LanguageElement), element.ElementType.ToString());
        }

        private string GetToolTipTextWithReturnType(LanguageElement element)
        {
            return string.Format("{0} {1} : {2}", GetMemberVisibilityText(element), element.ElementType.ToString(), GetTypeDeclarationText(element));
        }

        private string GetTypeDeclarationText(LanguageElement element)
        {
            string typeDeclaration = CodeRush.Language.GetSimpleTypeName(element.GetTypeName());

            if (typeDeclaration == string.Empty)
                typeDeclaration = "object";

            return typeDeclaration;
        }

        private TreeNode SetIconsForTreeNode(TreeNode node)
        {
            var memberElement = node.Tag as IMemberElement;
            var codeElement = node.Tag as CodeElement;

            if (memberElement == null && codeElement == null) return node;

            string imageKey = string.Empty;

            //default setting for icon is the combination of type + visibility
            if (memberElement != null)
            {
                if (_visibilitiesWithCustomIcons.Contains(memberElement.Visibility))
                    imageKey = string.Format("{0}_{1}", memberElement.ElementType.ToString(), GetMemberVisibilityText(memberElement));
                else
                    imageKey = string.Format("{0}_Public", memberElement.ElementType.ToString());
            }

            //if its a namespace, its not an IMemeberElement so no visibility settings...
            if (codeElement != null && codeElement.ElementType == LanguageElementType.Namespace)
                imageKey = "Scope_Namespace";

            node.ImageKey = imageKey;
            node.SelectedImageKey = imageKey;

            return node;
        }

    }
}
